﻿using OF.DistributeService.Core.Attribute;
using OF.DistributeService.Core.Common;
using OF.DistributeService.Core.Entity;
using OF.DistributeService.Core.Exception;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OF.DistributeService.Core.Client.Entity
{

    public class AppGroupMapInstanceList : ConcurrentDictionary<string, AppGroupInstanceListHolder>
    {
        private static List<InstanceServiceVersion> EmptyList = new List<InstanceServiceVersion>();

        private ClientConfig clientConfig;
        private ServiceOnOffWatch onOffWatch = new ServiceOnOffWatch();
        private ZooKeeperProxy zk;
        public AppGroupMapInstanceList(ClientConfig clientConfig)
        {
            this.clientConfig = clientConfig;
        }

        public void InitZk(ZooKeeperProxy zk)
        {
            this.zk = zk;
            onOffWatch.SetZk(this.zk, this);
        }

        public void Subscribe(ClientToAppConfig clientToAppConfig)
        {
            List<string> appGroupPathList = new List<string> { 
                clientConfig.OFDistributeServiceZKRootPath,
                clientToAppConfig.ServiceAppName,
                clientConfig.GroupName
            };
            string groupPath = string.Join("/", appGroupPathList);
            AppGroupInstanceListHolder instanceList = null;
            if (!this.TryGetValue(groupPath, out instanceList))
            {
                instanceList = new AppGroupInstanceListHolder(clientConfig, zk, clientToAppConfig, groupPath);
                this.AddOrUpdate(groupPath, instanceList, (k, v) => instanceList);
            }
            FetchServiceVersionList(groupPath);
        }

        public void FetchServiceVersionList(string groupPath)
        {
            AppGroupInstanceListHolder instanceListHolder = null;
            if (!this.TryGetValue(groupPath, out instanceListHolder))
            {
                return;
            }
            var nodeData = zk.Exists(groupPath, onOffWatch);
            if (nodeData == null)
            {
                instanceListHolder.SetInstanceList(EmptyList);
                return;
            }
            List<InstanceServiceVersion> cloneInstanceList = instanceListHolder.GetCloneInstanceList();
            List<string> newInstanceNodeList = zk.GetChildren(groupPath, onOffWatch).ToList();
            List<string> oldInstanceNodeList = cloneInstanceList.Select(item => item.encodeApiBaseUrl).ToList();
            var instanceModify = new InstanceListModify(oldInstanceNodeList, newInstanceNodeList);
            if (instanceModify.DeleteList != null && instanceModify.DeleteList.Count > 0)
            {
                foreach (var deleteItem in instanceModify.DeleteList)
                {
                    List<InstanceServiceVersion> tempList = cloneInstanceList.Where(item => item.encodeApiBaseUrl.Equals(deleteItem, StringComparison.InvariantCultureIgnoreCase)).ToList();
                    if (tempList != null && tempList.Count > 0)
                    {
                        foreach (var item in tempList)
                        {
                            cloneInstanceList.Remove(item);
                        }
                    }
                }
            }
            if (instanceModify.InsertList != null && instanceModify.InsertList.Count > 0)
            {
                foreach (string instance in instanceModify.InsertList)
                {
                    InstanceServiceVersion instanceVersion = instanceListHolder.NewInstanceServiceVersion(groupPath, instance);
                    cloneInstanceList.Add(instanceVersion);
                }
            }
            instanceListHolder.SetInstanceList(cloneInstanceList);
        }

        public OFDistributeServiceEndPointSearchResult FindEndPoint(OFDistributeServiceEndPointSearchRequest request)
        {
            List<string> serviceVersionPathList = new List<string> { 
                clientConfig.OFDistributeServiceZKRootPath,
                request.ServiceAppName,
                request.Group
            };
            string groupPath = string.Join("/", serviceVersionPathList);
            AppGroupInstanceListHolder appGroupInstanceList = null;
            if (this.TryGetValue(groupPath, out appGroupInstanceList))
            {
                List<OFDistributeServiceEndPointSearchResult> randomList = new List<OFDistributeServiceEndPointSearchResult>();
                var instanceList = appGroupInstanceList.GetInstanceList();
                foreach (var instance in instanceList)
                {
                    if (instance.IsOffLine())
                    {
                        continue;
                    }
                    string key = ClientToAppManager.GetKey(new string[] { request.ServiceName, request.Version });
                    ServiceVersionMethods serviceVersionMethods = null;
                    if (instance.TryGetValue(key, out serviceVersionMethods))
                    {
                        MethodWithHttpMethod[] methodArray = serviceVersionMethods.methodArray;
                        var methodList = methodArray.Where(method => method.MethodName.Equals(request.Method)).ToList();
                        if (methodList.Count == 1)
                        {
                            randomList.Add(new OFDistributeServiceEndPointSearchResult { MethodInfo = methodList[0], ServiceUrl = instance.apiBaseUrl + serviceVersionMethods.virtualPath });
                        }
                        else if (methodList.Count > 1)
                        {
                            throw new DuplicateServiceMethodException(request.Method);
                        }
                        else
                        {
                            Util.LogInfo("can't find method:" + request.Method + ",Serivce is:" + request.ServiceName + ",instance:" + instance.apiBaseUrl);
                        }
                    }
                }
                if (randomList.Count == 0)
                {
                    return null;
                }
                else if (randomList.Count == 1)
                {
                    return randomList[0];
                }
                else
                {
                    Random rnd = new Random();
                    int index = rnd.Next(randomList.Count);
                    return randomList[index];
                }
            }
            else
            {
                return null;
            }
        }
    }

}
